from __future__ import generators 
import random,sys
"""

Poset
=====

This file contains several utility functions to manipulate poset structures.
it defines types supported to represent resources.

"""

""" types in resources """
type_decoder = { "int":int, "float":float, "str":str}

def parse_line_poset(line):
	""" parse a line in a poset file """
	while line.find("  ") > -1: line = line.replace("  "," ")
	sp = line.strip().split(" ")
	if len(sp) < 2:return None,[]
	if len(sp) == 2:
		# name value1,value2,...
		name = sp[0]
		values = sp[1].split(",")
		return name,values
	elif len(sp) == 3:
		# name type value1,value2,...
		name = sp[0]
		typ = sp[1]
		if not type_decoder.has_key(typ):
			raise Exception("type not recognized: "+typ)
		decoder = type_decoder[typ]
		values_not_casted = sp[2].split(",")
		values= []
		if typ in ("int","float"):
			for val in values_not_casted:
				spp = val.split("-")
				if len(spp) == 1: values.append(  decoder(val))
				elif len(spp) == 3:
					deb = decoder( spp[0] )
					end = decoder( spp[2] )
					step  = decoder( spp[1] )
					cur = deb 
					if deb >= end: raise Exception(" invalid: "+ str(deb) +" is not < "+str(end))
					while cur <= end:
						values.append( cur )	
						cur += step
				else:
					raise Exception(" invalid range provided: "+str(val))
		else: values = [ decoder(x) for x in values_not_casted]	
		return name,values	
	else:
		raise Exception("format error: "+line)	

def build_poset(data):
	""" read a poset description from a file"""
	Poset = dict()
	for line in  data.split("\n"):
		if len(line.strip()) == 0: continue 
		attr,values = parse_line_poset(line)	
		if attr is None:
			raise Exception("cannot decode poset line: "+str(line))
		if Poset.has_key(attr):
			raise Exception("error in poset definition: duplicate attribute name '"+attr+"'")
		num  = str(len(Poset.keys())+1)
		if len(num) ==1 : num = "0"+num
		attr = num+"_"+attr
		Poset[attr] = values
	return Poset

def read_poset(file_path):
	""" read a poset description from a file"""
	f = file(file_path,"r")
	Poset = dict()
	try:
		line =f.readline()
		while len(line) > 0:
			line_to_parse = line[:-1].strip()
			if len(line_to_parse) == 0:
				line = f.readline()
				continue
			attr,values = parse_line_poset(line_to_parse)
			if attr is None:
				raise Exception("cannot decode poset line: "+str(line[:-1]))
			if Poset.has_key(attr):
				raise Exception("error in poset definition: duplicate attribute name '"+attr+"'")
			num  = str(len(Poset.keys())+1)
			if len(num) ==1 : num = "0"+num
			attr = num+"_"+attr
			Poset[attr] = values
			line =f.readline()
	finally: f.close()
	return Poset

def write_poset(Poset):
	from cStringIO import StringIO
	out = StringIO()
	keys= Poset.keys()
	keys.sort()
	for k in keys:
		tab = Poset[k]
		if len(tab) == 0:
			print >>out,k,"int"
		else:
			elem = tab[0]
			ty = "str"
			if type(elem) == type(0):
				ty = "int"
			print >>out,k,ty,",".join([str(v) for v in tab])
	return out.getvalue()

def example_poset():
	Poset =  dict()	
	Poset["cpu"] = range(1,6)
	Poset["memory"] = [100*x for x in range(1,10)]
	return Poset

def build(options):
	""" @return: poset description
                    the returned object is a dictionnary	 
                    - key = attribute name
                    - value = possible attribute values
	"""
	if not options.has_key("poset"):
		print >>sys.stderr,"poset not specified, using default one"
		#sys.stderr.flush()
		return example_poset()
	else:
		return read_poset(options["poset"])

#---------------------------------------------------------
# Appelee par handler handle_random_resource : genere population de ressources a partir de specification poset + taille de population
def random_populate(Poset,max):
	Resources = []
	for x in range(max):
		element = dict()
		element2 = []
		keys = Poset.keys()
		keys.sort()	
		for attribute_name in keys:
			attr_values = Poset[attribute_name]	
			val = random.choice(attr_values)
			element[attribute_name] = val
			element2.append(val)
		Resources.append(element)		
	return Resources	
	

# Appelee par handler 
def parse_line_resource(Poset,line2,keys,expected_len):
	while line2.find("  ") > -1: line2  = line2.replace("  "," ") 
	line2 = line2.strip()
	if len(line2) == 0 : return None 
	values = [x.strip() for x in line2.split(",")]
	if expected_len is not None and len(values) != expected_len:
		raise Exception("found resource with "+str(len(values))+" attributes, expected size is "+str(expected_len))
	else:
		values_ok = []
		myvalues = dict()
		for vali in range(len(values)):
			found = False
			val = str(values[vali])
			for x in Poset[keys[vali]]:
				if str(x) == val:
					found = True
					values_ok.append(x)
					myvalues[keys[vali]] = x
					break
			if not found:
				raise Exception("value not found: \""+str(val)+"\" for attribute "+keys[vali])
		#if len(values_ok) == expected_len:
		#Resources.append(values_ok)
		return myvalues


def read_resources(Poset,file_path):
	""" read a poset description from a file"""
	f = file(file_path,"r")
	Resources = []
	try:
		line =f.readline()
		keys =  Poset.keys()
		keys.sort()	
		expected_len = len(keys)	
		lineno = 0
		while len(line) > 0:
			lineno += 1
			line2 = line[:-1]
			try:
				res = parse_line_resource(Poset,line2,keys,expected_len)
				if res is not None:
					Resources.append(res)
			except Exception,e:
				raise Exception("while reading resource at line "+str(lineno)+", data=\""+line[:-1]+"\"\n"+str(e) )
			line =f.readline()
	finally: f.close()
	return Resources 


def build_resources(Poset,data):
	""" read a poset description from a file"""
	Resources = []
	keys =  Poset.keys()
	keys.sort()	
	expected_len = len(keys)	
	lineno = 0
	for line in data.split("\n"):
		lineno += 1
		try:
			res = parse_line_resource(Poset,line,keys,expected_len)
			if res is not None:
				Resources.append(res)
		except Exception,e:
			raise Exception("while reading resource at line "+str(lineno)+", data=\""+line+"\"\n"+str(e) )
	return Resources 

def resource_to_str(Poset,r):
	if type(r) == type(dict()):
		keys = r.keys()
		keys.sort()
		return ",".join( [str(r[k]) for k in keys])# + " ; " +str(keys)# + " ; "+str(r)+" ; " +str(keys) 
	else: raise Exception("error on resource")	
	#elif type(r) in (  type( (0,0)), type([]) ):
	#	return ",".join( [str(v) for v in r])

def resources_to_str(Poset,Resources):
	tab = []
	for r in Resources:
		tab.append( resource_to_str(Poset,r) )
	return "\n".join(tab)
			


def populate(Poset,options):
	""" return poset elements """
	#--------------------------------------------------------
	if not options.has_key("resources"):
		print >>sys.stderr,"resources not specified, generating random ones"
		if not options.has_key("n"): raise Exception("number of resources not defined")
		return random_populate(Poset,int(options["n"]))
	else:
		return read_resources(Poset,options["resources"])

#
#def value_compare(a,b):
#	""" 0: equal , 1: a < b, 2: a > b, -1: a and b not comparable"""
#	score = [0,0]
#	for xi in range(len(a)):
#		if a[xi] == b[xi]: continue
#		elif a[xi] < b[xi]: score[0] += 1
#		else: score[1] += 1
#	if score[0] == 0:
#		if score[1] == 0: return 0
#		else: return 2 
#	else:
#		if score[1] == 0: return 1
#		else: return -1 
#		
## Implemente l'operateur de comparaison du poset, associe a l ensemble des ressources (aka profils)
#def compare(a,b):
#        """ 0: equal , 1: a < b, 2: a > b, -1: a and b not comparable"""
#	if type(a) == type(dict()): 
#		k = a.keys()
#		a2 = [a[x] for x in k]
#		b2 = [b[x] for x in k]
#		return value_compare(a2,b2)
#	else:
#		return value_compare(a,b)
